package io.vertx.guides.wiki

import com.github.rjeschke.txtmark.Processor
import io.vertx.core.AbstractVerticle
import io.vertx.core.AsyncResult
import io.vertx.core.Future
import io.vertx.core.eventbus.DeliveryOptions
import io.vertx.core.eventbus.Message
import io.vertx.core.json.JsonObject
import io.vertx.core.logging.LoggerFactory
import io.vertx.ext.web.Router
import io.vertx.ext.web.RoutingContext
import io.vertx.ext.web.handler.BodyHandler
import io.vertx.ext.web.templ.FreeMarkerTemplateEngine
import java.util.*


/**
 * Created by ashye on 2017/7/18.
 */
class HttpServerVerticle : AbstractVerticle() {

    companion object {

        val LOGGER = LoggerFactory.getLogger(MainVerticle::class.java)

        val CONFIG_HTTP_SERVER_PORT = "http.server.port"
        val CONFIG_WIKIDB_QUEUE = "wikidb.queue"
    }

    private var wikiDbQueue = "wikidb.queue"

    override fun start(startFuture: Future<Void>) {
        wikiDbQueue = config().getString(CONFIG_WIKIDB_QUEUE, "wikidb.queue")

        val server = vertx.createHttpServer()

        val router = Router.router(vertx)
        router.get("/").handler(this::indexHandler)
        router.get("/wiki/:page").handler(this::pageRenderingHandler)
        router.post().handler(BodyHandler.create())
        router.post("/save").handler(this::pageUpdateHandler)
        router.post("/create").handler(this::pageCreateHandler)
        router.post("/delete").handler(this::pageDeletionHandler)

        val portNumber = config().getInteger(CONFIG_HTTP_SERVER_PORT, 8081)
        server.requestHandler(router::accept)
                .listen(portNumber) { ar ->
                    if (ar.succeeded()) {
                        LOGGER.info("HTTP server running on port 8081")
                        startFuture.complete()
                    }else {
                        LOGGER.error("Could not start a HTTP server")
                        startFuture.fail(ar.cause())
                    }
                }
    }

    /**
     * page and request handler
     */
    private val templateEngine = FreeMarkerTemplateEngine.create()

    private fun indexHandler(context: RoutingContext) {

        val options = DeliveryOptions().addHeader("action", "all-pages")
        vertx.eventBus().send(wikiDbQueue, JsonObject(), options) { reply: AsyncResult<Message<JsonObject>> ->
            if (reply.succeeded()) {
                val body = reply.result().body()
                context.put("title", "Wiki home")
                context.put("pages", body.getJsonArray("pages").list)

                templateEngine.render(context, "templates/index.ftl") { ar ->
                    if (ar.succeeded()) {
                        context.response().putHeader("Content-Type", "text/html")
                        context.response().end(ar.result())
                    }else {
                        context.fail(ar.cause())
                    }
                }
            }else {
                context.fail(reply.cause())
            }
        }
    }

    private val EMPTY_PAGE_MARKDOWN =
            "# A new page\n" +
             "\n" +
             "Feel-free to write in Markdown!\n"
    private fun pageRenderingHandler(context: RoutingContext) {
        val requestPage = context.request().getParam("page")

        val request = JsonObject().put("page", requestPage)
        val options = DeliveryOptions().addHeader("action", "get-page")
        vertx.eventBus().send(wikiDbQueue, request, options) { replay: AsyncResult<Message<JsonObject>> ->
            if (replay.succeeded()) {
                val body = replay.result().body()
                val found = body.getBoolean("found")
                val rawContent = body.getString("rawContent", EMPTY_PAGE_MARKDOWN)

                context.put("title", requestPage)
                context.put("id", body.getInteger("id", -1))
                context.put("newPage", if (found) "no" else "yes")
                context.put("rawContent", rawContent)
                context.put("content", Processor.process(rawContent));
                context.put("timestamp", Date().toString())

                templateEngine.render(context, "templates/page.ftl") { ar ->
                    if (ar.succeeded()) {
                        context.response().putHeader("Content-Type", "text/html")
                        context.response().end(ar.result())
                    }else {
                        context.fail(ar.cause())
                    }
                }
            }else {
                context.fail(replay.cause())
            }
        }
    }

    private fun pageCreateHandler(context: RoutingContext) {
        val pageName: String? = context.request().getParam("name")
        var location = "/wiki/$pageName"
        if (pageName.isNullOrEmpty()) {
            location = "/"
        }

        context.response().statusCode = 303
        context.response().putHeader("Location", location)
        context.response().end()
    }

    private fun pageUpdateHandler(context: RoutingContext) {

        val title = context.request().getParam("title")
        val request = JsonObject()
                .put("id", context.request().getParam("id"))
                .put("title", title)
                .put("markdown", context.request().getParam("markdown"))

        val options = DeliveryOptions()
        if ("yes" == context.request().getParam("newPage")) {
            options.addHeader("action", "create-page")
        }else {
            options.addHeader("action", "save-page")
        }

        vertx.eventBus().send(wikiDbQueue, request, options) { reply: AsyncResult<Message<JsonObject>> ->
            if (reply.succeeded()) {
                context.response().statusCode = 303
                context.response().putHeader("Location", "/wiki/" + title);
                context.response().end()
            } else {
                context.fail(reply.cause())
            }
        }
    }

    private fun pageDeletionHandler(context: RoutingContext) {
        val id = context.request().getParam("id")

        val request = JsonObject().put("id", id)
        val options = DeliveryOptions().addHeader("action", "delete-page")
        vertx.eventBus().send(wikiDbQueue, request, options) { reply: AsyncResult<Message<JsonObject>> ->
            if (reply.succeeded()) {
                context.response().statusCode = 303
                context.response().putHeader("Location", "/")
                context.response().end()
            } else {
                context.fail(reply.cause())
            }
        }
    }

}

